/*
 * Copyright (C) 2012-2013 Matthias Zeimer and the miniframes project group.
 * All rights reserved. 
 * 
 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/EPL-1.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.miniframes.minivalues;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * <p>
 * An unmodifiable array. These arrays have an equality check on, a hash-code
 * calculation on and a string enumeration of its contents.
 * 
 * <p>
 * An unmodifiable array behaves quite similar to a native array (see <a
 * href="#literature1">[1]</a>) <em>but</em> without the possibility of changing
 * its components assignment or its components order <em>and</em> with the
 * following behavioural differences to native arrays:
 * <dl>
 * <dt>{@link #equals(Object)}</dt>
 * <dd>Two unmodifiable arrays are said to be equal if and only if all their
 * components are considered as equal and ordered the same, instead of a simple
 * identity check.</dd>
 * <dt>{@link #hashCode()}</dt>
 * <dd>The hash-code is calculated overall components hash-codes instead of
 * returning the identity hash-code of this array instance.</dd>
 * <dt>{@link #toString()}</dt>
 * <dd>The string representation is a list of all components represented by
 * their string representation, instead of returning a String as described for
 * {@link Object#toString()}.</dd>
 * </dl>
 * </p>
 * 
 * <p>
 * An unmodifiable arrays is {@linkplain Iterable iterable} and for that it
 * could be used in a &quot;foreach&quot;-statement (see <a
 * href="#literature2">[2]</a>).
 * </p>
 * 
 * <h4><a name="serialisation">Serialisation of unmodifiable array</a></h4>
 * <p>
 * An unmodifiable array is serialisable if and only if its
 * {@linkplain #getComponentType() component type}, the type bound to type
 * variable T, is serialisable. Refer the documentation of {@link Serializable}
 * for more detailed discussion about serialisable types.
 * </p>
 * 
 * <h4><a name="literature">Literature</a></h4>
 * <dl>
 * <dt><a name="literature1">[1]</a></dt>
 * <dd>
 * <i>Java Language Specification</i>, Third Edition, Chapter 10 <i>Arrays</i>,
 * James Gosling, Bill Joy, Guy Steele and Gilad Bracha, ISBN 0-321-24678-0,
 * Online copy of this chapter available at: <a href
 * ="http://docs.oracle.com/javase/specs/jls/se5.0/html/arrays.html"
 * >http://docs.oracle.com/javase/specs/jls/se5.0/html/arrays.html</a> (last
 * visited: 29th March 2013)</dd>
 * <dt><a name="literature2">[2]</a></dt>
 * <dd>
 * <i>Java Language Specification</i>, Third Edition, Chapter 14.14.2 <i>The
 * enhanced for statement</i>, James Gosling, Bill Joy, Guy Steele and Gilad
 * Bracha, ISBN 0-321-24678-0, Online copy of this chapter available at: <a href
 * ="http://docs.oracle.com/javase/specs/jls/se5.0/html/statements.html#14.14.2"
 * >http://docs.oracle.com/javase/specs/jls/se5.0/html/statements.html#14.14.2
 * </a> (last visited: 4th June 2013)</dd>
 * </dl>
 * 
 * @author Matthias Zeimer
 * @version 3
 * @since 0.1, 14.01.2013
 * @param <T>
 *            The unmodifiable arrays {@linkplain #getComponentType() component
 *            type} (comparable to {@link Class#getComponentType()}).
 */
public final class UnmodifiableArray<T> implements Iterable<T>, Serializable {

    /**
     * The class versions UID for serialisation support.
     * 
     * @see Serializable
     */
    private static final long serialVersionUID = -8965411188716481505L;

    /**
     * <p>
     * Builds an unmodifiable array based on the passed source array.
     * </p>
     * 
     * <p>
     * The references in the internal representation of the returned
     * unmodifiable array are always a copy of the array passed on
     * {@code source}. The referenced data in each component of {@code source}
     * is not copied.
     * </p>
     * 
     * @param source
     *            The source array from which the new unmodifiable array is
     *            created, not <code>null</code>; <code>null</code>-components
     *            are permitted; a length of 0 is permitted.
     * @return An unmodifiable array based on {@code source}; not
     *         <code>null</code>.
     * @throws NullPointerException
     *             if {@code source} is <code>null</code>.
     * 
     * 
     *             FIXME CHANGE: handle null like described in
     *             {@link Value#Value(String, Object...)} for properties (better
     *             readabilitym more stable) Getting wired using final Object[]
     *             nullArray = null; UnmodifiableArray.valueOf(nullArray);
     *             result has one element null. but it using
     *             UnmodifiableArray.valueOf((Integer)null); would work! ->
     *             Inconsistency in Java
     *             UnmodifiableArray.valueOf((Object[])null); will not work AND
     *             UnmodifiableArray.valueOf(null); // == example before. is
     *             also not working.
     * 
     *             JLS Chapter 15.12.4.2 Evaluate Arguments (second paragraph
     *             ff.)
     */
    public static <T> UnmodifiableArray<T> valueOf(final T... source)
            throws NullPointerException {

        if (source == null) {
            throw new NullPointerException("Argument source may not be null."
                    + " If you like to create an array with only one"
                    + " component which is null please use a code like:"
                    + " new T[] {null} where T is to be replaced with"
                    + " the arrays component type.");
        }

        final Class<T> componentType = UnmodifiableArray
                .determineArrayComponentType(source);
        final T[] components = createNativeArrayOfComponentType(componentType,
                source.length);

        for (int index = 0; index < source.length; index++) {
            components[index] = componentType.cast(source[index]);
        }

        return new UnmodifiableArray<T>(componentType, components);
    }

    /**
     * <p>
     * Creates a native array of the specified {@linkplain #getComponentType()
     * component type}.
     * </p>
     * 
     * <p>
     * This is an unsafe cast but need to be valid through the arrays
     * definition.
     * </p>
     * 
     * @return An newly created native array with the length and component type
     *         specified; never <code>null</code>.
     */
    @SuppressWarnings("unchecked")
    private static <T> T[] createNativeArrayOfComponentType(
            final Class<T> componentType, final int length) {
        return (T[]) Array.newInstance(componentType, length);
    }

    /**
     * <p>
     * Determines the arrays component type and returns the class instance as
     * class of T.
     * </p>
     * 
     * <p>
     * This is an unsafe cast but need to be valid through the arrays
     * definition.
     * </p>
     * 
     * @param source
     *            The source array, not <code>null</code>.
     * @return the component class Class&lt;T&gt;, not <code>null</code>.
     */
    @SuppressWarnings("unchecked")
    private static <T> Class<T> determineArrayComponentType(final T... source) {
        return (Class<T>) source.getClass().getComponentType();
    }

    /**
     * The arrays components.
     * 
     * @see #componentType
     */
    private final T[] components;

    /**
     * The type of the arrays components.
     * 
     * @see #components
     */
    private final Class<T> componentType;

    /**
     * <p>
     * Constructs an instance during the de-serialisation process. This
     * constructor should never be used immediately, it was added to satisfy the
     * constraints described in {@link Serializable}.
     * </p>
     */
    private UnmodifiableArray() {
        this.componentType = null;
        this.components = null;
    }

    /**
     * <p>
     * Internally creates an instance.
     * </p>
     * 
     * @param componentType
     *            The type of the arrays components, not <code>null</code>.
     * @param components
     *            The arrays components, not <code>null</code>, possible with a
     *            length of 0.
     */
    private UnmodifiableArray(final Class<T> componentType, //
            final T[] components) {
        this.componentType = componentType;
        this.components = components;
    }

    /**
     * <p>
     * Returns the arrays component at the specified index.
     * </p>
     * 
     * @param index
     *            The index of the component to return which <em>must</em>
     *            satisfy: {@code index <} {@link #length()}
     *            {@code && index >= 0}.
     * @return The arrays component at specified index which might be
     *         <code>null</code>.
     * @throws ArrayIndexOutOfBoundsException
     *             if {@code index >=} {@link #length()} {@code || index < 0}.
     */
    public T componentAt(final int index) //
            throws ArrayIndexOutOfBoundsException {
        return this.componentType.cast(this.components[index]);
    }

    /**
     * <p>
     * Returns {@code true} if and only if {@code other} is an unmodifiable
     * array and it is equals to this one.
     * </p>
     * 
     * <p>
     * This and an other unmodifiable array are considered as equal if both
     * arrays contain the same number of components, and all corresponding pairs
     * of components in the two unmodifiable arrays are equal. These pairs are
     * considered as <em>equal</em> if and only if
     * {@code (cThis==null ? cOther==null : cThis.equals(cOther))}. In other
     * words, the two arrays are equal if they contain equal components in the
     * same order.
     * <p>
     * 
     * <p>
     * This methods implementation is inspired by
     * {@link Arrays#equals(Object[], Object[])}.
     * </p>
     * 
     * @param other
     *            An other unmodifiable array to be compared with <em>or</em>
     *            and other object or {@code null}.
     * @return {@code true} if and only if {@code other} is an unmodifiable
     *         array and this and the other are considered as equal (see methods
     *         description).
     */
    @Override
    public boolean equals(final Object other) {
        if (this == other) {
            return true;
        }
        if (other == null) {
            return false;
        }
        if (!(other instanceof UnmodifiableArray)) {
            return false;
        }
        // Because we compare the component-type there is not need for a cast to
        // UnmodifiableArray<T> which would be an unsafe one.
        final UnmodifiableArray<?> otherArray = (UnmodifiableArray<?>) other;
        if (!this.componentType.equals(otherArray.componentType)) {
            return false;
        }
        if (!Arrays.equals(this.components, otherArray.components)) {
            return false;
        }
        return true;
    }

    /**
     * <p>
     * Returns the component type of this array. This method behaves quite
     * similar as {@link Class#getComponentType()}.
     * </p>
     * 
     * @return The component type of this array. The returned class is never
     *         <code>null</code>. Subsequent calls will return the same
     *         instance.
     */
    public Class<T> getComponentType() {
        return this.componentType;
    }

    /**
     * <p>
     * Returns a hash code based on the contents of this unmodifiable array.
     * </p>
     * 
     * <p>
     * If this array contains native arrays as elements, the hash code is based
     * on their identities rather than their contents, <em>but</em> the hash
     * code of contained unmodifiable arrays will be calculated like described
     * here.
     * </p>
     * 
     * <p>
     * This methods implementation is inspired by
     * {@link Arrays#hashCode(Object[])}.
     * </p>
     * 
     * @return A content-based hash code for this unmodifiable array.
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + this.componentType.hashCode();
        result = (prime * result) + Arrays.hashCode(this.components);
        return result;
    }

    /**
     * <p>
     * Is this array empty? An unmodifiable array is said to be empty if and
     * only if it has a {@linkplain #length() length} of 0.
     * </p>
     * 
     * @return <code>true</code> if and only if this array is empty,
     *         <code>false</code> else.
     * @see <a href="#literature1">[1]</a>
     */
    public boolean isEmpty() {
        return this.length() == 0;
    }

    /**
     * <p>
     * Returns an {@link Iterator} over the components of this unmodifiable
     * array. The optional operation {@link Iterator#remove()} will constantly
     * throw an {@link UnsupportedOperationException}.
     * </p>
     * 
     * <p>
     * The returned Iterator is <em>not</em> thread-safe.
     * </p>
     * 
     * @return A newly created instance of an iterator over this arrays
     *         components; never <code>null</code>.
     * @see java.lang.Iterable#iterator()
     */
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {

            private int index = 0;

            @Override
            public boolean hasNext() {
                return this.index < UnmodifiableArray.this.length();
            }

            @Override
            public T next() {

                if (!this.hasNext()) {
                    throw new NoSuchElementException("Only " + this.index
                            + " components available");
                }

                final T nextComponent = UnmodifiableArray.this
                        .componentAt(this.index);
                this.index++;
                return UnmodifiableArray.this.getComponentType().cast(
                        nextComponent);
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException(
                        "remove() not supported for an iteration on an instance"
                                + " of UnmodifiableArray.");
            }
        };
    }

    /**
     * <p>
     * Returns the length of this array, which is the number of its components.
     * </p>
     * 
     * @return The length, an {@code int} which is {@code >= 0}.
     */
    public int length() {
        return this.components.length;
    }

    /**
     * <p>
     * Returns a modifiable copy of this unmodifiable array with the same
     * {@linkplain #getComponentType() component type} as this one. Changes on
     * returned array will not affect this array.
     * </p>
     * 
     * @return A newly created native array as copy of this one (initially it
     *         has the same length and the same components in the same order as
     *         this one). Subsequent calls will never return the same instance,
     *         a new copy will be created on every call. The returned array is
     *         never <code>null</code>.
     */
    public T[] toArray() {
        final T[] result = UnmodifiableArray.createNativeArrayOfComponentType(
                this.componentType, this.length());
        System.arraycopy(this.components, 0, result, 0, this.components.length);
        return result;
    }

    /**
     * <p>
     * Returns a {@link String} representation of the contents of this array.
     * The string representation consists of a list of the array's components,
     * enclosed in square brackets ({@code []}). Adjacent components are
     * separated by the String {@code ", "} (a comma followed by a space). The
     * components are converted to strings as by {@link String#valueOf(Object)}.
     * </p>
     * 
     * <p>
     * <em>Note:</em> If this unmodifiable array contains <i>native arrays</i>
     * as components, they are converted to a String, which describes their
     * <i>identities</i> rather than their contents.
     * </p>
     * 
     * <p>
     * This methods implementation is inspired by
     * {@link Arrays#toString(Object[])}.
     * </p>
     * 
     * @return The String representation. The returned String is never
     *         <code>null</code> and never {@linkplain String#isEmpty() empty}.
     *         If this array is {@linkplain #isEmpty() empty} the String
     *         {@code "[]"} is returned.
     */
    @Override
    public String toString() {
        final StringBuilder resultBuilder = new StringBuilder("[");
        boolean notFirstRun = false;

        for (final T eachComponent : this.components) {
            if (notFirstRun) {
                resultBuilder.append(", ");
            }

            resultBuilder.append(String.valueOf(eachComponent));

            notFirstRun = true;
        }

        return resultBuilder.append("]").toString();
    }
}
